//
// Logger.cpp
//
// $Id: //poco/1.4/Foundation/src/Logger.cpp#4 $
//
// Library: Foundation
// Package: Logging
// Module:  Logger
//
// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier:	BSL-1.0
//


#include "Poco/Logger.h"
#include "Poco/Exception.h"
#include "Poco/String.h"                // 比较有用，保留


namespace Poco {


Logger::LoggerMap* Logger::m_pLoggerMap = 0;
Mutex Logger::m_mapMtx;
const std::string Logger::ROOT;


Logger::Logger(const std::string& name, Channel* pChannel, int level,bool usingThread): 
m_name(name), m_pChannel(pChannel), m_level(level),m_threadOn(usingThread),m_running(false)
{
	if (pChannel) pChannel->duplicate();
	if (m_threadOn) 
	{
		m_cacheQueue = new std::list<LogMsg>();
		m_writeQueue = new std::list<LogMsg>();
		m_thread.start(*this);
	}
}


Logger::~Logger()
{
	if (m_pChannel) m_pChannel->release();
	if (m_threadOn)
	{
		m_running = false;
		m_thread.join();
		delete m_writeQueue;
		delete m_cacheQueue;
		m_writeQueue = NULL;
		m_cacheQueue = NULL;
	}
}

void Logger::run()
{
	m_running = true;
	while(m_running)
	{
		{
			ScopedLock<Mutex>  lock(m_msgQueueMutex);
			if (m_cacheQueue->empty())
			{
				Thread::sleep(100);
				continue;
			}
			std::swap(m_writeQueue,m_cacheQueue);
		}
		for (auto msg:*m_writeQueue)
		{
			if (msg.m_file.empty() || msg.m_line < 0)
			{
				log(msg.m_msg,msg.m_priority);
			}
			else
			{
				log(msg.m_msg,msg.m_priority,msg.m_file.c_str(),msg.m_line);
			}
		}
		m_writeQueue->clear();
	}
}

void Logger::setChannel(Channel* pChannel)
{
	if (m_pChannel) m_pChannel->release();
	m_pChannel = pChannel;
	if (m_pChannel) m_pChannel->duplicate();
}


Channel* Logger::getChannel() const
{
	return m_pChannel;
}


void Logger::setLevel(int level)
{
	m_level = level;
}


void Logger::setLevel(const std::string& level)
{
	setLevel(parseLevel(level));
}

void Logger::log(const Exception& exc)
{
	error(exc.displayText());
}


void Logger::log(const Exception& exc, const char* file, int line)
{
	error(exc.displayText(), file, line);
}

// void Logger::log(const string& msg)
// {
// 	m_pChannel->log(msg);
// }

void Logger::dump(const std::string& msg, const void* buffer, std::size_t length, LogPriority prio)
{
	if (m_level >= prio && m_pChannel)
	{
		std::string text(msg);
		formatDump(text, buffer, length);
		m_pChannel->log(text, prio);
	}
}


void Logger::setLevel(const std::string& name, int level)
{
	Mutex::ScopedLock lock(m_mapMtx);

	if (m_pLoggerMap)
	{
        auto it = m_pLoggerMap->find(name);
        if ( it != m_pLoggerMap->end())
        {
            it->second->setLevel(level);
        }
	}
}


void Logger::setChannel(const std::string& name, Channel* pChannel)
{
	Mutex::ScopedLock lock(m_mapMtx);

	if (m_pLoggerMap)
	{
		auto it = m_pLoggerMap->find(name);
        if ( it != m_pLoggerMap->end())
        {
            it->second->setChannel(pChannel);
        }
	}
}

void Logger::formatDump(std::string& message, const void* buffer, std::size_t length)
{
	const int BYTES_PER_LINE = 16;

	message.reserve(message.size() + length*6);
	if (!message.empty()) message.append("\n");
	unsigned char* base = (unsigned char*) buffer;
	int addr = 0;
	while (addr < length)
	{
		if (addr > 0) message.append("\n");
		message.append(Poco::format("%04x",addr));
		message.append("  ");
		int offset = 0;
		while (addr + offset < length && offset < BYTES_PER_LINE)
		{
			message.append(Poco::format("%02x",base[addr + offset]));
			message.append(offset == 7 ? "  " : " ");
			++offset;
		}
		if (offset < 7) message.append(" ");
		while (offset < BYTES_PER_LINE) { message.append("   "); ++offset; }
		message.append(" ");
		offset = 0;
		while (addr + offset < length && offset < BYTES_PER_LINE)
		{
			unsigned char c = base[addr + offset];
			message += (c >= 32 && c < 127) ? (char) c : '.';
			++offset;
		}
		addr += BYTES_PER_LINE;
	}
}


Logger& Logger::get(const std::string& name)
{
	Mutex::ScopedLock lock(m_mapMtx);

	return unsafeGet(name);
}
/*
void Logger::setProperty(const std::string& name, const std::string& value)
{}

std::string Logger::getProperty(const std::string& name)
{
    return "";
}

void Logger::log(const string& name,const string& msg,LogPriority prio)
{}

void Logger::log(const string& name,const string& msg,LogPriority prio,const char* file,int line)
{}
*/
Logger& Logger::unsafeGet(const std::string& name)
{
	Logger* pLogger = find(name);
	if (!pLogger)
	{
		if (name == ROOT)
		{
			pLogger = new Logger(name, 0, LOG_INFORMATION);
		}
		else
		{
			Logger& par = parent(name);
			pLogger = new Logger(name, par.getChannel(), par.getLevel());
		}
		add(pLogger);
	}
	return *pLogger;
}


Logger& Logger::create(const std::string& name, Channel* pChannel, int level,bool usingThread)
{
	Mutex::ScopedLock lock(m_mapMtx);

	if (find(name)) throw ExistsException();
	Logger* pLogger = new Logger(name, pChannel, level,usingThread);
	add(pLogger);
	return *pLogger;
}


Logger& Logger::root()
{
	Mutex::ScopedLock lock(m_mapMtx);

	return unsafeGet(ROOT);
}


Logger* Logger::has(const std::string& name)
{
	Mutex::ScopedLock lock(m_mapMtx);

	return find(name);
}


void Logger::shutdown()
{
	Mutex::ScopedLock lock(m_mapMtx);

	if (m_pLoggerMap)
	{
		for (LoggerMap::iterator it = m_pLoggerMap->begin(); it != m_pLoggerMap->end(); ++it)
		{
			it->second->m_pChannel->close();
		}
		delete m_pLoggerMap;
		m_pLoggerMap = 0;
	}
}


Logger* Logger::find(const std::string& name)
{
	if (m_pLoggerMap)
	{
		LoggerMap::iterator it = m_pLoggerMap->find(name);
		if (it != m_pLoggerMap->end())
			return it->second;
	}
	return 0;
}


void Logger::destroy(const std::string& name)
{
	Mutex::ScopedLock lock(m_mapMtx);

	if (m_pLoggerMap)
	{
		LoggerMap::iterator it = m_pLoggerMap->find(name);
		if (it != m_pLoggerMap->end())
		{
			it->second->m_pChannel->close();
			m_pLoggerMap->erase(it);
		}
	}
}


void Logger::names(std::vector<std::string>& names)
{
	Mutex::ScopedLock lock(m_mapMtx);

	names.clear();
	if (m_pLoggerMap)
	{
		for (LoggerMap::const_iterator it = m_pLoggerMap->begin(); it != m_pLoggerMap->end(); ++it)
		{
			names.push_back(it->first);
		}
	}
}


Logger& Logger::parent(const std::string& name)
{
	std::string::size_type pos = name.rfind('.');
	if (pos != std::string::npos)
	{
		std::string pname = name.substr(0, pos);
		Logger* pParent = find(pname);
		if (pParent)
			return *pParent;
		else
			return parent(pname);
	}
	else return unsafeGet(ROOT);
}


int Logger::parseLevel(const std::string& level)
{
	if (icompare(level, "none") == 0)
		return 0;
	else if (icompare(level, "fatal") == 0)
		return LOG_FATAL;
	else if (icompare(level, "critical") == 0)
		return LOG_CRITICAL;
	else if (icompare(level, "error") == 0)
		return LOG_ERROR;
	else if (icompare(level, "warning") == 0)
		return LOG_WARNING;
	else if (icompare(level, "notice") == 0)
		return LOG_NOTICE;
	else if (icompare(level, "information") == 0)
		return LOG_INFORMATION;
	else if (icompare(level, "debug") == 0)
		return LOG_DEBUG;
	else if (icompare(level, "trace") == 0)
		return LOG_TRACE;
	else
	{
		return -1;
	}
}


void Logger::add(Logger* pLogger)
{
	if (!m_pLoggerMap)
		m_pLoggerMap = new LoggerMap;
	m_pLoggerMap->insert(LoggerMap::value_type(pLogger->name(), pLogger));
}


} // namespace Poco
